package com.example.mq.mqServer;

import com.example.mq.mqServer.core.Binding;
import com.example.mq.mqServer.core.exchange.ExchangeType;
import com.example.mq.mqServer.core.message.Message;
import com.example.mq.mqServer.exception.MqException;
import lombok.extern.slf4j.Slf4j;

/**
 * @BelongsProject: 0.mq
 * @BelongsPackage: com.example.mq.mqServer.core
 * @Author : 游在海里的鱼
 * @CreateTime : 2023-12-16  20:15
 * @Version : 1.0
 * @Description : 路由
 */
@Slf4j
public class Router {

    /** 绑定Key和路由Key的定义规则:
     *      1.由[数字,字母,下划线,.]组成;[.]将bindingKey分隔成多个部分
     *      2.绑定Key支持两种通配符[*,#],通配符必须被[.]分隔成一个单独部分,[*]匹配1个单独部分,[#]匹配0到多个单独部分
     *  案例1: 格式
     *      bindingKey:aaa.*bb.ccc   =>格式不合法,[*]得是单独部分
     *      bindingKey:aaa.*.ccc     =>格式合法
     *      routingKey:aaa.#.ccc     =>格式不合法,路由Key不支持通配符
     *      routingKey:@aa.bbb.ccc     =>格式不合法,[@]非法
     *  案例2: 匹配
     *      bindingKey:aaa.bbb.ccc
     *      routingKey:aaa.bbb.ccc   => 匹配成功
     *  案例3: *
     *      bindingKey:aaa.*.ccc
     *      routingKey:aaa.bbb.ccc   => 匹配成功
     *      routingKey:aaa.b.b.b.ccc => 匹配失败,*只能匹配一个部分
     *      routingKey:aaa.ccc       => 匹配失败,*必须得匹配上一个部分
     *  案例4: #
     *      bindingKey:aaa.#.ccc
     *      routingKey:aaa.bbb.abc   => 匹配失败,[abc]匹配不是
     *      routingKey:aaa.bbb.ccc   => 匹配成功
     *      routingKey:aaa.b.b.b.ccc => 匹配成功
     *      routingKey:aaa.ccc       => 匹配成功
     */


    /** ---------------------------------------------------------------------------------------------------------------------
     * @Method  : 检查绑定的Key是否符合格式
     * @Describe: 这个key能使用通配符*和#
     * @modifier: [public] [实例方法]
    **/
    public  boolean checkBindingKey(String bindingKey){
        //绑定Key可以为空字符串[""],在直接交换机和扇形交换机中
        if(bindingKey == null || bindingKey.length()==0){
            log.debug("[检查绑定Key] [路由Key: "+bindingKey+"]检验无误");
            return true;
        }

        //尾字符不能是[.]
        if (bindingKey.charAt(bindingKey.length()-1) == '.'){
            log.error("[检查绑定Key] [绑定Key: "+bindingKey+"]中[字符.]不合法");
            return false;
        }
        
        //字符合法
        for(char c:bindingKey.toCharArray()){
            if((c>='0' && c<='9') || (c>='A' && c<='Z') || (c>='a' && c<='z') || (c=='_'||c=='.'||c=='*'||c=='#')){
                continue;
            }
            log.error("[检查绑定Key] [绑定Key: "+bindingKey+"]中[字符"+c+"]不合法");
            return false;
        }

        //分隔出的部分不为空且[*]和[#]作为一个独立部分
        String[] strings = bindingKey.split("\\.");
        for(String str:strings){
            int len = str.length();
            if(len == 0 || (len>1 && (str.contains("*") || str.contains("#")))){
                log.error("[检查绑定Key] [绑定Key: "+bindingKey+"]中["+str+"]不合法");
                return false;
            }
        }

        //排除这三种情况:{[aaa.#.#.bbb],[aaa.*.#.bbb],[aaa.#.*.bbb]}
        for(int i=0; i<strings.length-1; i++){
            if((strings[i].equals("#") && strings[i+1].equals("#"))
            || (strings[i].equals("#") && strings[i+1].equals("*"))
            || (strings[i].equals("*") && strings[i+1].equals("#"))){
                log.error("[检查绑定Key] [绑定Key: "+bindingKey+"]中["+strings[i]+"]不合法");
                return false;
            }
        }

        log.debug("[检查绑定Key] [绑定Key: "+bindingKey+"]检验无误");
        return true;
    }


    /** ---------------------------------------------------------------------------------------------------------------------
     * @Method  : 检验路由Key是否符合格式
     * @Describe: TODO
     * @modifier: [public] [实例方法]
    **/
    public boolean checkRoutingKey(String routingKey){
        //路由Key可以为空字符串[""]
        if(routingKey == null || routingKey.length()==0){
            log.debug("[检查路由Key] [路由Key: "+routingKey+"]检验无误");
            return true;
        }

        //尾字符不能是[.]
        if (routingKey.charAt(routingKey.length()-1) == '.'){
            log.error("[检查绑定Key] [绑定Key: "+routingKey+"]中[字符.]不合法");
            return false;
        }
        //检验字符是否合法
        for(char c:routingKey.toCharArray()){
            if((c>='0' && c<='9') || (c>='A' && c<='Z') || (c>='a' && c<='z') || (c=='_'||c=='.')){
                continue;
            }
            log.error("[检查路由Key] [路由Key: "+routingKey+"]中[字符"+c+"]不合法");
            return false;
        }
        //分隔出的部分不为空
        String[] strings = routingKey.split("\\.");
        for(String str:strings){
            int len = str.length();
            if(len == 0 ){
                log.error("[检查路由Key] [路由Key: "+routingKey+"]中["+str+"]不合法");
                return false;
            }
        }
        log.debug("[检查路由Key] [路由Key: "+routingKey+"]检验无误");
        return true;
    }

    /** ---------------------------------------------------------------------------------------------------------------------
     * @Method  : 路由匹配绑定
     * @Describe: TODO
     * @modifier: [public] [实例方法]
    **/
    public boolean router(ExchangeType exchangeType, Binding binding, Message message){
        if(exchangeType == ExchangeType.FANOUT){
            return true;
        }else if(exchangeType == ExchangeType.TOPIC){
            return routeTopic(binding,message);
        }else {
            new MqException("[Router] [路由匹配] [交换机类型]非法").printStackTrace();
            return false;
        }
    }

    private boolean routeTopic(Binding binding, Message message){
        String[] bindingKeys = binding.getBindingKey().split("\\.");
        String[] routingKeys = message.getRoutingKey().split("\\.");
        int n = bindingKeys.length;
        int m = routingKeys.length;

        boolean[][] dp = new boolean[m + 1][n + 1];
        dp[0][0] = true;
        for (int i = 1; i <= n; ++i) {
            if (bindingKeys[i - 1].equals("#")) {
                dp[0][i] = true;
            } else {
                break;
            }
        }
        for (int i = 1; i <= m; ++i) {
            for (int j = 1; j <= n; ++j) {
                if (bindingKeys[j - 1].equals("#")) {
                    dp[i][j] = dp[i][j - 1] || dp[i - 1][j];
                } else if (bindingKeys[j - 1].equals("*")  || routingKeys[i-1].equals(bindingKeys[j - 1])) {
                    dp[i][j] = dp[i - 1][j - 1];
                }
            }
        }
        if(dp[m][n]){
            log.debug("[Router] [路由匹配] [路由Key: "+message.getRoutingKey()+"]匹配[绑定Key: :"+binding.getBindingKey()+"]成功");
            return true;
        }
        log.error("[Router] [路由匹配] [路由Key: "+message.getRoutingKey()+"]匹配[绑定Key: :"+binding.getBindingKey()+"]失败");
        return false;
    }
}
